home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / contrib / carmel / c-client / carmel.c < prev    next >
C/C++ Source or Header  |  1994-08-31  |  35KB  |  1,233 lines

  1. /*----------------------------------------------------------------------
  2.  
  3.          T H E   C A R M E L   M A I L   D R I V E R
  4.  
  5.   Author(s):   Laurence Lundblade
  6.                Baha'i World Centre
  7.                Data Processing
  8.                Haifa, Israel
  9.             Internet: lgl@cac.washington.edu or laurence@bwc.org
  10.                September 1992
  11.  
  12.   Last Edited: Aug 31, 1994
  13.  
  14. The "Carmel" mail format is cutsom mail file format that has messages
  15. saved in individual files and an index file that references them. It
  16. has also been called the "pod" or "vmail" format. This is probably not
  17. of interest to anyone away from it's origin, though the Carmel2 format
  18. might be.
  19.  
  20. This driver reads and writes the format in conjunction with the carmel2
  21. driver. The carmel2 file driver does most of the work as most of the
  22. operations are done on the created auxiliary carmel2 index and the
  23. carmel index is updated when the mail file is closed.
  24.  
  25.   ----------------------------------------------------------------------*/
  26.  
  27.  
  28. #include <stdio.h>
  29. #include <ctype.h>
  30. #include <pwd.h>
  31. #include <netdb.h>
  32. #include <errno.h>
  33. extern int errno;        /* just in case */
  34. #include "mail.h"
  35. #include "osdep.h"
  36. #include <sys/dir.h>
  37. #include <sys/file.h>
  38. #include <sys/stat.h>
  39. #include <sys/time.h>
  40. #include "carmel2.h"
  41. #include "carmel.h"
  42. #include "rfc822.h"
  43. #include "misc.h"
  44.  
  45.  
  46. /* Driver dispatch used by MAIL. The carmel 2driver shares most of
  47.    its data structures and functions with the carmel driver
  48.  */
  49.  
  50. DRIVER carmeldriver = {
  51.   "carmel",
  52.   (DRIVER *) NIL,        /* next driver */
  53.   carmel_valid,            /* mailbox is valid for us */
  54.   carmel_parameters,            /* Set parameters */
  55.   carmel_find,            /* find mailboxes */
  56.   carmel_find_bboards,        /* find bboards */
  57.   carmel_find_all,              /* find all mailboxes */
  58.   carmel_find_bboards,          /* find all the bboards ; just a noop here  */
  59.   carmel_subscribe,            /* subscribe to mailbox */
  60.   carmel_unsubscribe,            /* unsubscribe from mailbox */
  61.   carmel_subscribe_bboard,    /* subscribe to bboard */
  62.   carmel_unsubscribe_bboard,    /* unsubscribe from bboard */
  63.   carmel_create,        /* create mailbox */
  64.   carmel_delete,        /* delete mailbox */
  65.   carmel_rename,        /* rename mailbox */
  66.   carmel_open,            /* open mailbox */
  67.   carmel_close,            /* close mailbox */
  68.   carmel2_fetchfast,        /* fetch message "fast" attributes */
  69.   carmel2_fetchflags,        /* fetch message flags */
  70.   carmel2_fetchstructure,    /* fetch message envelopes */
  71.   carmel2_fetchheader,        /* fetch message header only */
  72.   carmel2_fetchtext,        /* fetch message body only */
  73.   carmel2_fetchbody,        /* fetch message body section */
  74.   carmel2_setflag,        /* set message flag */
  75.   carmel2_clearflag,        /* clear message flag */
  76.   carmel2_search,        /* search for message based on criteria */
  77.   carmel2_ping,            /* ping mailbox to see if still alive */
  78.   carmel_check,                    /* check for new messages */
  79.   carmel_expunge,        /* expunge deleted messages */
  80.   carmel2_copy,            /* copy messages to another mailbox */
  81.   carmel2_move,            /* move messages to another mailbox */
  82.   carmel_append,                /* Append message to a mailbox */
  83.   carmel2_gc,            /* garbage collect stream */
  84. };
  85.  
  86. MAILSTREAM carmelproto ={&carmeldriver}; /* HACK for default_driver in pine.c*/
  87.  
  88. #ifdef ANSI
  89. static int   carmel_sift_files(struct direct *); 
  90. static void  carmel_recreate_index(MAILSTREAM *);
  91. static char  *carmel_calc_paths(int, char *, int);
  92. static int   vmail2carmel(char *, char *, MAILSTREAM *, char *, char *);
  93. static int   write_carmel_index(FILE *, MESSAGECACHE *, ENVELOPE *);
  94. static int   carmel_reset_index_list();
  95. static void  carmel_kill_locks(char *);
  96. #else
  97. static int   carmel_sift_files(); 
  98. static void  carmel_recreate_index();
  99. static char  *carmel_calc_paths();
  100. static int   vmail2carmel();
  101. static int   write_carmel_index();
  102. static int   carmel_reset_index_list();
  103. static void  carmel_kill_locks();
  104. #endif
  105.  
  106.  
  107.  
  108. extern char carmel_20k_buf[20000], carmel_path_buf[], carmel_error_buf[];
  109.  
  110. static int   disk_setup_checked = 0;
  111.  
  112. /*----------------------------------------------------------------------
  113.   Carmel mail validate mailbox
  114.  
  115. Args: name -- name to check
  116.  
  117. Returns: our driver if name is valid, otherwise calls valid in next driver
  118.  ---*/
  119.  
  120. DRIVER *carmel_valid (name)
  121.     char *name;
  122. {
  123.     return(carmel_isvalid(name) ? &carmeldriver : NIL);
  124. }
  125.  
  126.  
  127.  
  128. /*----------------------------------------------------------------------
  129.     Check and see if a named mailbox is a valid carmel mail index
  130.  
  131. Args: name -- name or path of mail file. It is a FQN, e.g. #carmel#sent-mail
  132.  
  133. Returns:  0 if it is not a valid mailbox
  134.           1 if it is.
  135.       
  136.    A carmel index must be a regular file, readable, and have the second word in
  137. the file be "index", upper or lower case.
  138.   ----*/
  139. int 
  140. carmel_isvalid (name)
  141.     char *name;
  142. {
  143.     struct stat     sbuf;
  144.     int             fd;
  145.     char           *p, *carmel_index_file;
  146.     struct carmel_mb_name *parsed_name;
  147.  
  148.     if(!disk_setup_checked){
  149.        disk_setup_checked = 1;
  150.        carmel_init(NULL);
  151.     }
  152.  
  153.     /* Don't accept plain "inbox".  If we do then the carmel driver
  154.        takes over all other drivers linked after it.  This way the 
  155.        inbox-path in Pine can be set to #carmel#MAIL to make the
  156.        inbox the carmel one 
  157.       */
  158.  
  159.     parsed_name = carmel_parse_mb_name(name, '\0');
  160.     if(parsed_name == NULL)
  161.       return(0);
  162.  
  163.     carmel_free_mb_name(parsed_name);
  164.  
  165.     carmel_index_file = carmel_calc_paths(CalcPathCarmelIndex, name, 0);
  166.  
  167.     if(carmel_index_file == NULL)
  168.       return(0); /* Will get two error messages here, one from dummy driver */
  169.  
  170.     if(stat(carmel_index_file, &sbuf) < 0)
  171.       return(1); /* If the name matches and it doesn't exist, it's OK */
  172.   
  173.     if(!(sbuf.st_mode & S_IFREG))
  174.       return(0);
  175.   
  176.     fd = open(carmel_index_file, O_RDONLY);
  177.     if(fd < 0)
  178.       return(0);
  179.   
  180.     if(read(fd, carmel_20k_buf, 200) <= 0)
  181.       return(0);
  182.     carmel_20k_buf[199] = '\0';
  183.   
  184.     close(fd);
  185.   
  186.     for(p = carmel_20k_buf; *p && !isspace(*p); p++); /* frist word */
  187.     for(; *p && isspace(*p); p++);  /* space */
  188.     lcase(p);                       /* lower case the "index" */
  189.     if(!*p || strncmp(p, "index", 5))  
  190.       return(0);
  191.   
  192.     return(1);
  193. }
  194.  
  195.  
  196.  
  197.  
  198. /*----------------------------------------------------------------------
  199.  
  200.   ----*/
  201. void *
  202. carmel_parameters(function, value)
  203.      long  function;
  204.      char *value;
  205. {}
  206.  
  207.  
  208.  
  209. /*----------------------------------------------------------------------
  210.    This is used by scandir to determine which files in the directory
  211.  are treated as mail files
  212.   ----*/
  213. static char *sift_pattern = NULL;
  214. static int
  215. carmel_sift_files(dir)
  216.      struct direct *dir;
  217. {
  218.     if(dir->d_name[0] == '.')
  219.       return(0);
  220.     else if(strcmp(dir->d_name, "MAIL") == 0) 
  221.      /* Never return the inbox. "INBOX" is always included by default */
  222.       return(0);
  223.     else if(pmatch(dir->d_name, sift_pattern))
  224.       return(1);
  225.     else
  226.       return(0);
  227. }
  228.  
  229. int alphasort();
  230.  
  231. /* ----------------------------------------------------------------------
  232.   Carmel find list of mail boxes
  233.   Args: mail stream
  234.         pattern to search
  235.  
  236.   This scans the ".vmail/index" directory for a list of folders
  237.   (indexes in vmail terms).
  238.  
  239. BUG -- doesn't really match patterns (yet)
  240.  ----*/
  241. void 
  242. carmel_find(stream, pat)
  243.     MAILSTREAM *stream;
  244.     char *pat;
  245. {
  246.     char            tmp[MAILTMPLEN];
  247.     struct direct **namelist, **n;
  248.     int             num;
  249.     struct carmel_mb_name *parsed_name;
  250.     struct passwd         *pw;
  251.  
  252.     parsed_name = carmel_parse_mb_name(pat, '\0');
  253.  
  254.     if(parsed_name == NULL)
  255.       return;
  256.  
  257.     if(parsed_name->user == NULL) {
  258.         sprintf(tmp, "%s/%s", myhomedir(), CARMEL_INDEX_DIR);
  259.     } else {
  260.         pw = getpwnam(parsed_name->user);
  261.         if(pw == NULL) {
  262.             sprintf(carmel_error_buf,
  263.                   "Error accessing mailbox \"%s\". No such user name \"%s\"\n",
  264.                     pat, parsed_name->user);
  265.             mm_log(carmel_error_buf, ERROR);
  266.             return;
  267.         }
  268.         sprintf(tmp, "%s/%s", pw->pw_dir, CARMEL_INDEX_DIR);
  269.     }
  270.  
  271.     sift_pattern = parsed_name->mailbox;
  272.  
  273.     num = scandir(tmp, &namelist, carmel_sift_files, alphasort);
  274.  
  275.     if(num <= 0) {
  276. /*        sprintf(carmel_error_buf, "Error finding mailboxes \"%s\": %s",
  277.                 pat, strerror(errno));
  278.         mm_log(carmel_error_buf, ERROR);*/
  279.         return;
  280.     }
  281.  
  282.     for(n = namelist; num > 0; num--, n++) {
  283.         if(parsed_name->user == NULL) {
  284.             sprintf(tmp, "%s%s%c%s", CARMEL_NAME_PREFIX, parsed_name->version,
  285.                     CARMEL_NAME_CHAR, (*n)->d_name);
  286.         } else {
  287.             sprintf(tmp, "%s%s%c%s%c%s", CARMEL_NAME_PREFIX,
  288.                     parsed_name->version, CARMEL_NAME_CHAR,
  289.                     parsed_name->user, CARMEL_NAME_CHAR,
  290.                     (*n)->d_name);
  291.         }
  292.     mm_mailbox(tmp);
  293.     free(*n); 
  294.     }
  295.     free(namelist);
  296.     carmel_free_mb_name(parsed_name);
  297. }
  298.  
  299.  
  300. /*----------------------------------------------------------------------
  301.      Find_all is the same for find in the carmel driver; there is no 
  302. difference between subscribed and unsubscribed mailboxes
  303.   ----*/     
  304. void 
  305. carmel_find_all (stream, pat)
  306.     MAILSTREAM *stream;
  307.     char *pat;
  308. {
  309.    carmel_find(stream, pat);
  310. }
  311.  
  312.  
  313. /*----------------------------------------------------------------------
  314.    Find bulliten boards is a no-op for carmel, there are none (yet)
  315.  */
  316. void 
  317. carmel_find_bboards (stream,pat)
  318.      MAILSTREAM *stream;
  319.      char *pat;
  320. {
  321.   /* Always a no-op */
  322. }
  323.  
  324.  
  325. /*----------------------------------------------------------------------
  326.    No subscription management for the carmel format. Probably should work
  327.  with the .mailbox list format, but that's probably implemented in mail.c
  328.  ----*/
  329. long carmel_subscribe() {}
  330. long carmel_unsubscribe() {}
  331. long carmel_subscribe_bboard() {}
  332. long carmel_unsubscribe_bboard() {}
  333.  
  334.  
  335.  
  336. /*----------------------------------------------------------------------
  337.     Create a carmel folder
  338.  
  339. Args:  stream --  Unused here
  340.        mailbox -- the FQN of mailbox to create
  341.  
  342. Returns:  T on success, NIL on failure. 
  343.  
  344. An error message is logged on failure.
  345.   ----*/
  346. long
  347. carmel_create(stream, mailbox)
  348.      MAILSTREAM *stream;
  349.      char       *mailbox;
  350. {
  351.     char       *carmel_index_file, *carmel2_index_file;
  352.     struct stat sb;
  353.     FILE       *f;
  354.  
  355.     carmel_index_file = carmel_calc_paths(CalcPathCarmelIndex, mailbox, 0);
  356.  
  357.     if(carmel_index_file == NULL)
  358.       return(NIL);
  359.  
  360.     /*--- Make sure it doesn't already exist ---*/
  361.     if(stat(carmel_index_file, &sb) >= 0)
  362.       return(NIL);
  363.  
  364.     /*--- The carmel index ----*/
  365.     f = fopen(carmel_index_file, "w");
  366.     if(f == NULL) 
  367.       goto bomb;
  368.  
  369.     if(fprintf(f, "%-13.13s index.....\n",carmel_pretty_mailbox(mailbox))==EOF)
  370.       goto bomb;
  371.  
  372.     if(fclose(f) == EOF)
  373.       goto bomb;
  374.  
  375.     /*--- The carmel2 index ----*/
  376.     carmel2_index_file = carmel_calc_paths(CalcPathCarmel2Index, mailbox,0);
  377.     f = fopen(carmel2_index_file, "w");
  378.     if(f == NULL)
  379.       goto bomb;
  380.  
  381.     if(fprintf(f, "\254\312--CARMEL-MAIL-FILE-INDEX--\n") == EOF)
  382.       goto bomb;
  383.  
  384.     if(fclose(f) == EOF)
  385.       goto bomb;
  386.  
  387.  
  388.     carmel_kill_locks(mailbox); /* Get rid of any left over locks */
  389.     carmel_reset_index_list();
  390.     return(T);
  391.  
  392.   bomb:
  393.     unlink(carmel_calc_paths(CalcPathCarmelIndex, mailbox, 0));
  394.     sprintf(carmel_error_buf, "Error creating mailbox %s: %s",
  395.         carmel_pretty_mailbox(mailbox), strerror(errno));
  396.     mm_log(carmel_error_buf, ERROR);
  397.     return(NIL);
  398. }
  399.  
  400.  
  401.  
  402. /*----------------------------------------------------------------------
  403.     Delete a carmel mailbox, and it's carmel2 index
  404.  
  405. Args: stream --  unsed here
  406.       mailbox -- FQN of mailbox to delete
  407.  
  408. Returns: NIL -- if delete fails 
  409.          T   -- if successful
  410.   ----*/
  411. long
  412. carmel_delete(stream, mailbox)
  413.      MAILSTREAM *stream;
  414.      char       *mailbox;
  415. {
  416.     char *carmel_index_file, *carmel2_index_file;
  417.  
  418.     carmel_index_file = carmel_calc_paths(CalcPathCarmelIndex, mailbox, 0);
  419.     if(carmel_index_file == NULL)
  420.       return(NIL);
  421.  
  422.     if(unlink(carmel_index_file) < 0) {
  423.     sprintf(carmel_error_buf, "Error deleting mailbox %s: %s",
  424.         carmel_pretty_mailbox(mailbox), strerror(errno));
  425.     mm_log(carmel_error_buf, ERROR); 
  426.     return(NIL);
  427.     }
  428.  
  429.     /*------ Second the carmel2 index, quietly -----*/
  430.     carmel2_index_file = carmel_calc_paths(CalcPathCarmel2Index, mailbox, 0);
  431.  
  432.     unlink(carmel2_index_file);
  433.  
  434.     carmel_kill_locks(mailbox); /* Make sure all locks are clear */
  435.     carmel_reset_index_list();
  436.     return(T);
  437. }
  438.  
  439.  
  440.  
  441. /*----------------------------------------------------------------------
  442.    Rename a carmel index, and its carmel2 index
  443.  
  444. Args:  stream -- unused
  445.        orig   -- FQN of original mailbox 
  446.        new    -- FQN of new name
  447.  
  448. Returns: NIL if rename failed, T if it succeeded.
  449.  
  450. BUG: theoretically this could rename a folder from one user name 
  451. to another. Either that should be disallowed here, or code to make
  452. it work should be written.
  453.   ----*/
  454. long
  455. carmel_rename(stream, orig, new)
  456.      MAILSTREAM *stream;
  457.      char       *orig;
  458.      char       *new;
  459. {
  460.     char path_buf[CARMEL_PATHBUF_SIZE], *new_path, *orig_index_file;
  461.  
  462.     /*---- First the Carmel index -----*/
  463.     orig_index_file = carmel_calc_paths(CalcPathCarmelIndex, orig, 0);
  464.     if(orig_index_file == NULL)
  465.       return(NIL);
  466.     strcpy(path_buf, orig_index_file);
  467.     new_path = carmel_calc_paths(CalcPathCarmelIndex, new, 0);
  468.     if(new_path == NULL)
  469.       return(NIL);
  470.  
  471.     if(rename(path_buf, new_path) < 0) {
  472.     sprintf(carmel_error_buf, "Error renaming mailbox %s: %s",
  473.         carmel_pretty_mailbox(orig), strerror(errno));
  474.     mm_log(carmel_error_buf, ERROR); 
  475.     return(NIL);
  476.     }
  477.  
  478.     /*----- Next the Carmel index, quietly ------*/
  479.     strcpy(path_buf, carmel_calc_paths(CalcPathCarmel2Index, orig, 0));
  480.     new_path = carmel_calc_paths(CalcPathCarmel2Index, new, 0);
  481.  
  482.     rename(path_buf, new_path);
  483.  
  484.     carmel_reset_index_list();
  485.     return(T);
  486. }
  487.  
  488.  
  489.  
  490. /*----------------------------------------------------------------------
  491.   Carmel mail open
  492.  
  493.  Args: stream 
  494.  
  495.  Returns: stream on success, NIL on failure.
  496.  
  497. The complex set of comparison determine if we get it for read, write or not
  498. at all.
  499.  
  500. The folder will be open for write if:
  501.    - It is not locked
  502.    - The carmel index is writeable 
  503.    - The carmel index is writeable
  504.    - The carmel index is openable and not corrupt
  505.  
  506. The folder open will fail if:
  507.    - The carmel index does exist
  508.    - The carmel carmel index is missing
  509.         AND the folder is locked or unwritable
  510.  
  511. The folder is opened readonly if:
  512.    - It is locked
  513.    - The carmel index is not writable
  514.    - The carmel index is not writable
  515.  
  516.  ----*/
  517.  
  518. MAILSTREAM *
  519. carmel_open(stream)
  520.     MAILSTREAM *stream;
  521. {
  522.     char            carmel_index_path[CARMEL_PATHBUF_SIZE];
  523.     char            carmel2_index_path[CARMEL_PATHBUF_SIZE];
  524.     char            tmp[MAILTMPLEN], tmp2[MAILTMPLEN];
  525.     struct stat     carmel_stat, carmel2_stat;
  526.     int             carmel2_up_to_date;
  527.  
  528.     if(!stream) 
  529.       return(&carmelproto); /* Must be a OP_PROTOTYPE call */
  530.  
  531.     mailcache = carmel2_cache;
  532.  
  533.     /* close old file if stream being recycled */
  534.     if (LOCAL) {
  535.         carmel_close (stream);        /* dump and save the changes */
  536.         stream->dtb = &carmeldriver;    /* reattach this driver */
  537.         mail_free_cache (stream);    /* clean up cache */
  538.     }
  539.  
  540.     /* Allocate local stream */
  541.     stream->local              = fs_get (sizeof (CARMEL2LOCAL));
  542.     LOCAL->carmel              = 1;
  543.     LOCAL->msg_buf             = NULL;
  544.     LOCAL->msg_buf_size        = 0;
  545.     LOCAL->buffered_file       = NULL;
  546.     LOCAL->msg_buf_text_start  = NULL;
  547.     LOCAL->msg_buf_text_offset = 0;
  548.     LOCAL->stdio_buf           = NULL;
  549.     LOCAL->dirty               = NIL;
  550.     stream->msgno              = -1;
  551.     stream->env                = NULL;
  552.     stream->body               = NULL;
  553.     stream->scache             = 1;
  554.     LOCAL->calc_paths          = carmel_calc_paths;
  555.     LOCAL->aux_copy            = carmel_copy;
  556.     LOCAL->index_stream        = NULL;
  557.     LOCAL->new_file_on_copy    = 0;
  558.  
  559.     /*------ Figure out the file paths -------*/
  560.     if(carmel_calc_paths(CalcPathCarmelIndex,stream->mailbox,0) == NULL) 
  561.       return(NULL);
  562.     strcpy(carmel_index_path,
  563.        carmel_calc_paths(CalcPathCarmelIndex,stream->mailbox,0));
  564.     strcpy(carmel2_index_path,
  565.        carmel_calc_paths(CalcPathCarmel2Index,stream->mailbox,0));
  566.  
  567.     /*------ Does the CARMEL index exist? Fail if not ------*/
  568.     if(stat(carmel_index_path, &carmel_stat) < 0) {
  569.     sprintf(carmel_error_buf, "Can't open mailbox: %s", strerror(errno));
  570.     mm_log(carmel_error_buf, ERROR);
  571.         return(NULL);  /* FAIL, no carmel index */
  572.     }
  573.  
  574.     /*----- Determine if carmel index is up to date -----*/
  575.     if(stat(carmel2_index_path, &carmel2_stat) >= 0 &&
  576.        carmel2_stat.st_mtime >= carmel_stat.st_mtime)
  577.       carmel2_up_to_date = 1;
  578.     else
  579.       carmel2_up_to_date = 0;
  580.  
  581.     /*------ Do we have write access to the Carmel mail index? ----*/
  582.     if(access(carmel2_index_path, W_OK) != 0 && errno != ENOENT)
  583.       stream->readonly = 1; /* fail later if dir index is uncreatable */
  584.  
  585.     /*------ If CARMEL index R/O and Carmel out of date then fail -----*/
  586.     if(stream->readonly && !carmel2_up_to_date) {
  587.     mm_log("Can't open mailbox; Unable to write carmel index", ERROR);
  588.         return(NULL);
  589.     }
  590.  
  591.     /*-------- Case for R/O stream or failed lock ---------*/
  592.     if(stream->readonly ||
  593.        carmel2_lock(stream->local, stream->mailbox, READ_LOCK)< 0){
  594.     /* If carmel is out of date here that's OK, we just will see old data*/
  595.     if(access(carmel_index_path, R_OK) == 0) {
  596.         stream->readonly = 1;
  597.           goto open_it;
  598.     } else {
  599.         /*-- Can't lock, and there's no carmel index, best to fail -*/
  600.         mm_log("Can't open mailbox, can't lock to create carmel index",
  601.            ERROR);
  602.           return(NULL);
  603.         }
  604.     } 
  605.     /* Index is now read locked */
  606.  
  607.     /*---- Got an up to date carmel index and write access too ----*/
  608.     if(carmel2_up_to_date)
  609.       if(access(carmel2_index_path, W_OK) == 0) {
  610.           goto open_it;
  611.       } else {
  612.       stream->readonly = 1;
  613.       goto open_it;
  614.       }
  615.  
  616.     /*---- If needed recreation of carmel index fails, go readonly -----*/
  617.     if(vmail2carmel(carmel_index_path, carmel2_index_path, stream,
  618.                      stream->mailbox) < 0) {
  619.     carmel2_unlock(stream->local, stream->mailbox, READ_LOCK);
  620.         stream->readonly = 1;
  621.     }
  622.  
  623.   open_it:
  624.     /*-- carmel_open2 shouldn't fail after all this check, but just in case -*/
  625.     if(carmel_open2(stream, carmel2_index_path) < 0) {
  626.     /* carmel_open2 will take care of the error message */
  627.     carmel2_unlock(stream->local, stream->mailbox, READ_LOCK);
  628.     if (LOCAL->msg_buf) fs_give ((void **) &LOCAL->msg_buf);
  629.                     /* nuke the local data */
  630.     fs_give ((void **) &stream->local);
  631.         return(NULL);
  632.     }
  633.  
  634.     if(stream->readonly)
  635.     mm_log("Mailbox opened READONLY", WARN);
  636.  
  637.     mail_exists (stream,stream->nmsgs);
  638.     mail_recent (stream,stream->recent);
  639.  
  640.     return(stream);
  641. }
  642.  
  643.  
  644.  
  645. /*----------------------------------------------------------------------
  646.    Create a carmel2 index for a carmel index
  647.   
  648.  Args: folder        -- the full UNIX path name existing carmel folder
  649.        carmel_folder -- the full UNIX path name of carmel index to create
  650.        stream        -- MAILSTREAM to operate on
  651.        
  652.  Returns: 0 if all is OK
  653.          -1 if it failed 
  654.  
  655.   This reads the Carmel index, and the headers of the carmel messages to 
  656. construct entries for a carmel index.
  657.  ----*/
  658. static
  659. vmail2carmel(folder, carmel2_folder, stream, mailbox)
  660.      MAILSTREAM *stream;
  661.      char       *folder, *carmel2_folder;
  662.      char       *mailbox;
  663. {
  664.     FILE        *carmel_stream;
  665.     FILE        *carmel2_stream;
  666.     ENVELOPE    *e;
  667.     BODY        *b;
  668.     MESSAGECACHE mc;
  669.     char         pathbuf[CARMEL_PATHBUF_SIZE], *message, *p, *save_subject;
  670.     struct stat  st;
  671.     STRING       string_struct;
  672.  
  673.     sprintf(pathbuf, "Recreating Carmel2 index for \"%s\"...",
  674.             carmel_pretty_mailbox(mailbox));
  675.     mm_log(pathbuf, WARN);
  676.  
  677.     mm_critical(stream);
  678.     if(carmel2_lock(stream->local, mailbox, WRITE_LOCK) < 0)
  679.       return(-1);
  680.     
  681.     carmel_stream = fopen(folder,"r");
  682.     if(carmel_stream == NULL) {
  683.     carmel2_unlock(stream->local, mailbox, WRITE_LOCK);
  684.         mm_nocritical(stream);
  685.         return(-1);
  686.     }
  687.  
  688.     carmel2_stream = fopen(carmel2_folder, "w");
  689.     if(carmel2_stream == NULL) {
  690.     carmel2_unlock(stream->local, mailbox, WRITE_LOCK);    
  691.         mm_nocritical(stream);
  692.         return(-1);
  693.     }
  694.  
  695.     fprintf(carmel2_stream, "\254\312--CARMEL-MAIL-FILE-INDEX--\n");
  696.  
  697.     /* The first ....  line */
  698.     fgets(carmel_20k_buf, sizeof(carmel_20k_buf), carmel_stream); 
  699.  
  700.     /*---- Loop through all entries in carmel index -----*/
  701.     while(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),carmel_stream) != NULL){
  702.  
  703.     mc.data1      = 0L;
  704.     mc.data2      = atol(carmel_20k_buf); /* The file name/number */
  705.     mc.deleted    = 0;
  706.     mc.seen       = 1;
  707.     mc.flagged    = 0;
  708.     mc.answered   = 0;
  709.     mc.recent     = 0;
  710.     mc.user_flags = 0;
  711.  
  712.     strcpy(pathbuf,carmel_calc_paths(CalcPathCarmel2Data,
  713.                      mailbox, mc.data2));
  714.  
  715.     if(stat(pathbuf, &st) >= 0 ||
  716.        stat(strcat(pathbuf, ".wid"), &st) >= 0) {
  717.         save_subject = cpystr(carmel_20k_buf + 27);
  718.         save_subject[strlen(save_subject) - 1] = '\0';
  719.             mc.rfc822_size = st.st_size;
  720.             message = carmel2_readmsg(stream, 1, 0, mc.data2);
  721.             INIT(&string_struct, mail_string, (void *)"", 0);
  722.         rfc822_parse_msg(&e, &b, message, strlen(message), &string_struct,
  723.                  mylocalhost(), carmel_20k_buf);
  724.         carmel2_parse_bezerk_status(&mc, message);
  725.         carmel2_rfc822_date(&mc, message);
  726.         if(e->subject == NULL ||
  727.          strncmp(save_subject,e->subject,strlen(save_subject))){
  728.         if(e->subject != NULL)
  729.           fs_give((void **)(&(e->subject)));
  730.         if(strlen(save_subject))
  731.           e->subject = cpystr(save_subject);
  732.         else
  733.           e->subject = NULL;
  734.         }
  735.     } else {
  736.             /* Data file is missing; copy record as best we can */
  737.         carmel_20k_buf[strlen(carmel_20k_buf) - 1] = '\0';
  738.             e = mail_newenvelope();
  739.         e->subject = cpystr(carmel_20k_buf+27);
  740.         e->from  = mail_newaddr();
  741.         e->from->mailbox = cpystr(carmel_20k_buf+17);
  742.         for(p = e->from->mailbox; *p && !isspace(*p); p++);
  743.         *p = '\0';
  744.         mc.hours     = 1;
  745.         mc.minutes   = 0;
  746.         mc.seconds   = 0;
  747.         mc.zoccident = 0;
  748.         mc.zhours    = 0;
  749.         mc.zminutes  = 0;
  750.         mc.day       = 1;
  751.         mc.month     = 1;
  752.         mc.year      = 1;
  753.     }
  754.     carmel2_write_index(e, &mc, carmel2_stream);
  755.     mail_free_envelope(&e);
  756.     }
  757.  
  758.     fclose(carmel_stream);
  759.     fclose(carmel2_stream);
  760.  
  761.     carmel2_unlock(stream->local, mailbox, WRITE_LOCK);
  762.     mm_nocritical(stream);
  763.  
  764.     mm_log("Recreating Carmel2 index.....Done", WARN);
  765.  
  766.     return(1);
  767. }
  768.  
  769.  
  770.  
  771. #define TESTING /* Make a copy of carmel index before rewriting */
  772.  
  773. /*----------------------------------------------------------------------`
  774.    Close a carmel mail folder
  775.  
  776. Args: stream -- Mail stream to close
  777.  
  778. This will cause the carmel index to be recreated
  779.  
  780.   ----*/
  781. void 
  782. carmel_close(stream)
  783.     MAILSTREAM *stream;
  784. {
  785.     if (LOCAL && LOCAL->index_stream != NULL) {
  786.         /* only if a file is open */
  787.         if(!stream->readonly) {
  788.             carmel_check (stream);        /* dump final checkpoint */
  789.             carmel2_unlock(stream->local, stream->mailbox, READ_LOCK);
  790.         }
  791.  
  792.         fclose(LOCAL->index_stream);
  793.         if (LOCAL->msg_buf) fs_give ((void **) &LOCAL->msg_buf);
  794.           if(LOCAL->stdio_buf)
  795.           fs_give ((void **) &LOCAL->stdio_buf);
  796.     fs_give ((void **) &stream->local);
  797.  
  798.     }
  799.     stream->dtb = NIL;        /* log out the DTB */
  800. }
  801.  
  802.  
  803.  
  804. /*----------------------------------------------------------------------
  805.   Check for new mail and write out the indexes as a checkpoint
  806.  
  807. Args -- The stream to checkpoint
  808.  ----*/
  809. void
  810. carmel_check(stream)
  811.      MAILSTREAM *stream;
  812. {
  813.     if(carmel2_check2(stream))
  814.       carmel_recreate_index(stream); /* only if carmel2 index changed */
  815. }
  816.  
  817.  
  818.  
  819.  
  820. /*----------------------------------------------------------------------
  821.    Expunge the mail stream
  822.  
  823. Args -- The stream to checkpoint
  824.  
  825. Here we expunge the carmel2 index and then recreate the carmel index from
  826. it. 
  827.  ----*/
  828. void
  829. carmel_expunge(stream)
  830.      MAILSTREAM *stream;
  831. {
  832.     carmel2_expunge(stream);
  833.     carmel_recreate_index(stream); 
  834. }
  835.  
  836.  
  837.  
  838. /*----------------------------------------------------------------------
  839.    Rewrite the carmel index from the carmel2 index
  840.  
  841. Args: -- stream  to be recreated
  842.  
  843. BUG: could probably use some error checking here
  844.   ----*/
  845. static void
  846. carmel_recreate_index(stream)
  847.      MAILSTREAM *stream;
  848. {
  849. #ifdef TESTING
  850.     char          copy1[CARMEL_PATHBUF_SIZE];
  851.     char          copy2[CARMEL_PATHBUF_SIZE];
  852. #define MAXBACKUPS 4
  853.     int nback;
  854. #endif
  855.     long          msgno;
  856.     MESSAGECACHE *mc;
  857.     ENVELOPE     *envelope;
  858.     FILE         *carmel_index;
  859.     struct stat   sb;
  860.     char         *cm_index_file;
  861.     struct timeval tp[2];
  862.  
  863.     mm_critical(stream);
  864.  
  865.     /*---- calculate the carmel index path -----*/
  866.     strcpy(carmel_path_buf,
  867.            (*(LOCAL->calc_paths))(CalcPathCarmelIndex, stream->mailbox, 0));
  868.  
  869.     /*---- Get the first index line out of old index ----*/
  870.     *carmel_20k_buf = '\0';
  871.     carmel_index = fopen(carmel_path_buf, "r");
  872.     if(carmel_index != NULL) {
  873.         fgets(carmel_20k_buf,sizeof(carmel_20k_buf), carmel_index);
  874.         fclose(carmel_index);
  875.     }
  876.  
  877. #ifdef TESTING
  878.     /*--- rotate Backup copies of the index --- crude*/
  879.     for (nback = MAXBACKUPS; 1 < nback; --nback) {
  880.       strcpy(copy2,
  881.            (*(LOCAL->calc_paths))(CalcPathCarmelBackup, stream->mailbox, nback));
  882.       strcpy(copy1,
  883.            (*(LOCAL->calc_paths))(CalcPathCarmelBackup, stream->mailbox, nback-1));
  884.       unlink(copy2);
  885.       link(copy1,copy2);
  886.       }
  887.     unlink(copy1);
  888.     link(carmel_path_buf, copy1);
  889.     unlink(carmel_path_buf);
  890. #endif        
  891.  
  892.     /*---- Reopen the carmel index for write, truncating it ------*/        
  893.     carmel_index = fopen(carmel_path_buf, "w");
  894.  
  895.     if(carmel_index == NULL) 
  896.     goto bomb;
  897.     
  898.     if(fputs(carmel_20k_buf, carmel_index) == EOF)
  899.       goto bomb;
  900.  
  901.     /*---- Loop through all the message the stream has ----*/        
  902.     for(msgno = 1; msgno <= stream->nmsgs; msgno++) {
  903.         mc = MC(msgno);
  904.         envelope = carmel2_fetchstructure(stream, msgno, NULL);
  905.         if(write_carmel_index(carmel_index, mc, envelope) < 0)
  906.           goto bomb;
  907.     }
  908.     if(fclose(carmel_index) == EOF)
  909.       goto bomb;
  910.  
  911.     /*---- Get mod time of carmel index ---*/
  912.     cm_index_file = (*(LOCAL->calc_paths))(CalcPathCarmelIndex,
  913.                        stream->mailbox, 0);
  914.     if(stat(cm_index_file, &sb) >= 0) {
  915.     tp[0].tv_sec  = sb.st_atime;
  916.     tp[0].tv_usec = 0;
  917.     tp[1].tv_sec  = sb.st_mtime;
  918.     tp[1].tv_usec = 0;
  919.     
  920.     /*---- Set Carmel index to have same mod time ---*/
  921.     utimes(stream->mailbox, tp);
  922.     }
  923.     mm_nocritical(stream);
  924.  
  925. #ifdef CHATTY
  926.     sprintf(carmel_error_buf, "Rewrote Carmel index \"%s\" with %d messages",
  927.         carmel_pretty_mailbox(stream->mailbox), msgno-1);
  928.     mm_log(carmel_error_buf, WARN);
  929. #endif
  930.  
  931.     return;
  932.  
  933.   bomb:
  934.     mm_nocritical(stream);
  935.     sprintf(carmel_error_buf, "Error recreating carmel index: %s",
  936.            strerror(errno));
  937.     mm_log(carmel_error_buf, ERROR);
  938. }
  939.  
  940.  
  941.  
  942. /*----------------------------------------------------------------------
  943.   Do all the file name generation for the carmel driver
  944.  
  945. Args: operation -- The type of calculation to perform
  946.       mailbox   -- The canonical mailbox name
  947.       data_file_num -- For calculating the name of a file storing a message
  948.  
  949. Returns:  string with the path name in static storage or NULL on error
  950.  
  951. The Path passed in will be in the format #carmel#user-id#folder or
  952.                                          #carmel#folder
  953.  
  954. The only that error occurs is in looking up the user-id. A error is logged
  955. when that happens.
  956.   ----*/
  957. static char *
  958. carmel_calc_paths(operation, mailbox, data_file_num)
  959.      char *mailbox;
  960.      int   data_file_num, operation;
  961. {
  962.     static char    path[CARMEL_PATHBUF_SIZE];
  963.     char          *home_dir, *end_user_name;
  964.     struct passwd *pw;
  965.     struct carmel_mb_name *parsed_name;
  966.  
  967.     parsed_name = carmel_parse_mb_name(mailbox, '\0');
  968.  
  969.     if(parsed_name == NULL) {
  970.         mm_log("Internal error (bug). Invalid Carmel folder name",ERROR);
  971.         return(NULL);
  972.     }
  973.  
  974.     if(parsed_name->user != NULL) {
  975.         /*---- has a user in mailbox name -----*/
  976.         pw = getpwnam(parsed_name->user);
  977.         if(pw == NULL) {
  978.             sprintf(carmel_error_buf,
  979.                   "Error accessing mailbox \"%s\". No such user name \"%s\"\n",
  980.                     mailbox, parsed_name->user);
  981.             mm_log(carmel_error_buf, ERROR);
  982.             carmel_free_mb_name(parsed_name);
  983.             return(NULL);
  984.         }
  985.         home_dir = pw->pw_dir;
  986.     } else {
  987.         home_dir = myhomedir();
  988.     }
  989.     mailbox  = parsed_name->mailbox;
  990.  
  991.     if(strucmp2(mailbox, "inbox") == 0) /* Map "inbox" into "MAIL" */
  992.       mailbox = "MAIL";
  993.     
  994.  
  995.     switch(operation) {
  996.  
  997.       case CalcPathCarmelIndex:
  998.         sprintf(path, "%s/%s/%s", home_dir, CARMEL_INDEX_DIR, mailbox);
  999.         break;
  1000.  
  1001.       case CalcPathCarmelBackup:
  1002.         sprintf(path, "%s/%s/.%s.COD-FUR-%d", home_dir, CARMEL_INDEX_DIR,
  1003.                 mailbox, data_file_num);
  1004.         break;
  1005.  
  1006.       case CalcPathCarmel2Data:
  1007.     sprintf(path, "%s/%s/%d", home_dir, CARMEL_MSG_DIR, data_file_num);
  1008.     break;
  1009.  
  1010.       case CalcPathCarmel2Index:
  1011.         sprintf(path, "%s/%s/.%s.cx", home_dir, CARMEL_INDEX_DIR, mailbox);
  1012.         break;
  1013.  
  1014.       case CalcPathCarmel2MAXNAME:
  1015.         sprintf(path, "%s/%s", home_dir, CARMEL_MAXFILE);
  1016.         break;
  1017.  
  1018.       case CalcPathCarmel2WriteLock:
  1019.         sprintf(path, "%s/%s/.%s.wl", home_dir, CARMEL_INDEX_DIR, mailbox);
  1020.         break;
  1021.  
  1022.       case CalcPathCarmel2ReadLock:
  1023.         sprintf(path, "%s/%s/.%s.rl", home_dir, CARMEL_INDEX_DIR, mailbox);
  1024.         break;
  1025.     }
  1026.  
  1027. /*    sprintf(carmel_debug, "CARMEL: calc_paths returning \"%s\"\n", path);
  1028.     mm_dlog(carmel_debug);   */
  1029.  
  1030.     carmel_free_mb_name(parsed_name);
  1031.  
  1032.     return(path);
  1033. }
  1034.  
  1035.  
  1036.  
  1037. /*----------------------------------------------------------------------
  1038.     Copy carmel messages, this is the auxiliary copy, called from carmel2_copy
  1039.  
  1040. Args:  local -- Fake CARMELLOCAL structure
  1041.        mailbox -- Destination mailbox, in FQN format, e.g. #carmel#fooo
  1042.        e       -- envelope
  1043.        mc      -- MESSAGECACHE entry
  1044.  
  1045. Retuns: -1 on failure
  1046.   ----*/
  1047. long
  1048. carmel_copy(local, mailbox, e, mc)
  1049.      CARMEL2LOCAL *local;
  1050.      char         *mailbox;
  1051.      ENVELOPE     *e;
  1052.      MESSAGECACHE *mc;
  1053. {
  1054.     FILE       *carmel_index;
  1055.     int         new;
  1056.     struct stat sb;
  1057.     char        *carmel_index_file;
  1058.  
  1059.     carmel_index_file = (*(local->calc_paths))(CalcPathCarmelIndex, mailbox,0);
  1060.     if(carmel_index_file == NULL)
  1061.       return(-1);
  1062.     
  1063.     if(stat(carmel_index_file, &sb) < 0)
  1064.       new = 1;
  1065.     else
  1066.       new = 0;
  1067.  
  1068.     carmel_index = fopen(carmel_index_file, "a+");
  1069.     if(carmel_index == NULL)
  1070.       return(-1);
  1071.     
  1072.     if(new)
  1073.       fprintf(carmel_index, "%-13.13s index.....\n",
  1074.               carmel_pretty_mailbox(mailbox));
  1075.  
  1076.     if(write_carmel_index(carmel_index, mc, e) < 0) {
  1077.     fclose(carmel_index);
  1078.     return(-1);
  1079.     }
  1080.  
  1081.     if(fclose(carmel_index) == EOF)
  1082.       return(-1);
  1083.  
  1084.     return(0);
  1085. }
  1086.  
  1087.  
  1088.     
  1089. /*----------------------------------------------------------------------
  1090.     Make sure all the directories are there and writable for carmel
  1091.  
  1092. Args: folders_dir   - suggested directory, ignored by carmel
  1093.  
  1094. Result: returns 0 if OK, -1 if not
  1095.  ----*/
  1096. carmel_init(folders_dir)
  1097.      char *folders_dir;
  1098. {
  1099.     char  *p;
  1100.     FILE  *mail_file;
  1101.     struct stat sb;
  1102.  
  1103.     /*----- The ~/.vmail directory ----*/
  1104.     sprintf(carmel_path_buf, "%s/%s", myhomedir(), CARMEL_DIR);
  1105.     carmel2_check_dir(carmel_path_buf);
  1106.  
  1107.     /*---- The ~/.vmail/.MAXNAME file ------*/
  1108.     sprintf(carmel_path_buf,"%s/%s", myhomedir(), CARMEL_MAXFILE);
  1109.     if(stat(carmel_path_buf, &sb) < 0) {
  1110.     mail_file = fopen(carmel_path_buf, "w");
  1111.     if(mail_file == NULL) {
  1112.         mm_log("Error creating .MAXNAME file", ERROR);
  1113.     } else {
  1114.         fprintf(mail_file, "100000");
  1115.         fclose(mail_file);
  1116.     }
  1117.     }
  1118.  
  1119.     /*----- The ~/.vmail/index directory -----*/
  1120.     sprintf(carmel_path_buf,"%s/%s",myhomedir(),CARMEL_INDEX_DIR);
  1121.     carmel2_check_dir(carmel_path_buf);
  1122.  
  1123.     /*----- The ~/.vmail/msg directory -----/*
  1124.     sprintf(carmel_path_buf,"%s/%s",myhomedir(),CARMEL_MSG_DIR);
  1125.     carmel2_check_dir(carmel_path_buf);
  1126.  
  1127.     /*----- The ~/.vmail/MAIL file -----*/
  1128.     sprintf(carmel_path_buf, "%s/%s/MAIL", myhomedir(),  CARMEL_INDEX_DIR);
  1129.     if(stat(carmel_path_buf, &sb) < 0) {
  1130.         mail_file = fopen(carmel_path_buf, "w");
  1131.     if(mail_file == NULL) {
  1132.         mm_log("Error creating \"MAIL\" folder", WARN);
  1133.     } else {
  1134.          fprintf(mail_file, "MAIL          index.....\n");
  1135.         fclose(mail_file);
  1136.     }
  1137.     }
  1138.  
  1139.     return(0);
  1140. }
  1141.  
  1142.  
  1143.  
  1144. /*----------------------------------------------------------------------
  1145.    Write an entry into a carmel index
  1146.  
  1147. Args: file -- The open file stream
  1148.       mc   -- the MESSAGECACHE 
  1149.       envelope -- The envelope to write
  1150.  
  1151. Returns: 0 if all is OK, -1 if not
  1152.   ----*/    
  1153. static     
  1154. write_carmel_index(file, mc, envelope)
  1155.      FILE         *file;
  1156.      MESSAGECACHE *mc;
  1157.      ENVELOPE     *envelope;
  1158. {
  1159.     if(fprintf(file, "%6d %02d %-.3s %2d %-9.9s %-.50s\n",
  1160.            mc->data2, mc->day,
  1161.            month_abbrev2(mc->month),
  1162.            (mc->year + 1969) % 100,
  1163.            envelope != NULL && envelope->from != NULL &&
  1164.            envelope->from->mailbox != NULL ?
  1165.              envelope->from->mailbox :  "",
  1166.            envelope != NULL && envelope->subject != NULL ?
  1167.              envelope->subject : "") == EOF)
  1168.       return(-1);
  1169.     else
  1170.       return(0);
  1171. }
  1172.  
  1173.  
  1174.  
  1175. /*----------------------------------------------------------------------
  1176.     Append a message to a carmel mailbox
  1177.  
  1178. Args: stream  --  a suggested stream, ignored here
  1179.       mailbox --  FQN of mailbox to append message to
  1180.       message --  Text string of message
  1181.  
  1182. Returns: T on success, NIL on failure
  1183.   ----*/    
  1184. long
  1185. carmel_append(stream, mailbox, flags, date, message)
  1186.      MAILSTREAM *stream;
  1187.      char       *mailbox, *flags, *date;
  1188.      STRING     *message;
  1189. {
  1190.     CARMEL2LOCAL local;
  1191.  
  1192.     /*---- A fake local data structure to pass to other functions---*/
  1193.     local.calc_paths = carmel_calc_paths;
  1194.     local.carmel     = 1;
  1195.     local.aux_copy   = carmel_copy;
  1196.  
  1197.  
  1198.     return(carmel2_append2(stream, &local, mailbox, flags, date, message));
  1199. }
  1200.  
  1201.  
  1202.  
  1203.  
  1204.  
  1205. /*----------------------------------------------------------------------
  1206.   This really just removes the file Carmel uses for the list of 
  1207.  folders, because Carmel will just recreate it from the indexes themselves.
  1208.   ----*/
  1209. static 
  1210. carmel_reset_index_list()
  1211. {
  1212.     sprintf(carmel_path_buf, "%s/.inda", myhomedir());
  1213.     unlink(carmel_path_buf);
  1214.  
  1215.     sprintf(carmel_path_buf, "%s/.indf", myhomedir());
  1216.     unlink(carmel_path_buf);
  1217. }
  1218.  
  1219.  
  1220.  
  1221. /*----------------------------------------------------------------------
  1222.     Used to make sure there are no old locks of any sort left around 
  1223.  when a folder is deleted or when one is created. 
  1224.   --*/
  1225. static void   
  1226. carmel_kill_locks(mailbox)
  1227.      char *mailbox;
  1228. {
  1229.     unlink(carmel_calc_paths(CalcPathCarmel2WriteLock, mailbox, 0));
  1230.     unlink(carmel_calc_paths(CalcPathCarmel2ReadLock, mailbox, 0));
  1231.     unlink(carmel_calc_paths(CalcPathCarmel2Expunge, mailbox, 0));
  1232. }
  1233.